import pandas as pd
import lightgbm as lgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
import matplotlib.pyplot as plt
import joblib  # 用于导出模型
import numpy as np
from lightgbm import log_evaluation, early_stopping
from sklearn.preprocessing import LabelEncoder

def rmse(y_true, y_pred):
    """
    计算 RMSE
    :param y_true: 真实值，数组或列表
    :param y_pred: 预测值，数组或列表
    :return: RMSE 值
    """
    return np.sqrt(np.mean((np.array(y_true) - np.array(y_pred)) ** 2))

# 读取数据
df = pd.read_csv('lilylikes-all.csv', low_memory=False, encoding='GBK')
df['date_id'] = pd.to_datetime(df['date_id'],format='%Y%m%d')
df['first_new_date'] = pd.to_datetime(df['first_new_date'],format='%Y/%m/%d')
df['new_days'] = (df['date_id'] - df['first_new_date']).dt.days
# df['date_id'] = pd.to_datetime(df['date_id'],format='%Y%m%d')
# # 生成完整的日期范围
# all_dates = pd.date_range(start=df['date_id'].min(), end=df['date_id'].max(), freq='D')
# # 为每个SKU创建完整的日期索引
# df = df.set_index('date_id').groupby('sku').apply(
#     lambda x: x.reindex(all_dates, fill_value=np.nan)
# ).reset_index()
# # 填充缺失值（假设缺失日期的销量为0，或使用插值）
# df['sales'] = df['sales'].fillna(0)
# # 或使用前向填充：data_complete['sales_qty'].fillna(method='ffill')

df.set_index('date_id', inplace=True)
# 添加时间特征
df['day_of_week'] = df.index.dayofweek
df['day_of_month'] = df.index.day
df['month'] = df.index.month
# 将sku转换为category类型
df['middle_class_name'] = df['middle_class_name'].astype('category').cat.codes


le = LabelEncoder()
df["product_code"] = le.fit_transform(df["product_code"])
df["product_code"] = df["product_code"].astype("category").cat.codes
print(df.dtypes)




# 特征和目标变量
features = ['middle_class_name','new_days','day_of_week', 'day_of_month', 'month']
X = df[features]
y = df['sale_qty']


# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 创建 LightGBM 数据集
train_data = lgb.Dataset(X_train, label=y_train)
test_data = lgb.Dataset(X_test, label=y_test, reference=train_data)

# 定义 LightGBM 参数
params = {
    'objective': 'regression',  # 回归任务
    'metric': 'rmse',           # 评估指标为均方根误差
    'boosting_type': 'gbdt',    # 使用 GBDT 算法
    'num_leaves': 31,           # 叶子节点数
    'learning_rate': 0.05,      # 学习率
    'feature_fraction': 0.9,    # 特征采样比例
    'bagging_fraction': 0.8,    # 数据采样比例
    'bagging_freq': 5,          # 每 5 次迭代进行一次 bagging
    'verbose': -1,               # 不输出日志
    # 'categorical_feature': ['middle_class_name'] # 关键参数
}

callbacks = [log_evaluation(period=100), early_stopping(stopping_rounds=30)]

# 训练模型
model = lgb.train(
    params,
    train_data,
    num_boost_round=100,        # 迭代次数
    valid_sets=[test_data]
    # ,
    # callbacks=callbacks
)

# 导出模型到文件
# model_filename = '../lightgbm_model.pkl'
# joblib.dump(model, model_filename)
# print(f"模型已导出到文件：{model_filename}")

# 预测测试集
y_pred = model.predict(X_test, num_iteration=model.best_iteration)

# 评估模型
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"均方误差（MSE）：{mse}")
print(f"R2 分数：{r2}")
print("RMSE:", rmse(y_test, y_pred))

# 生成未来14天的日期
future_dates = pd.date_range(start=df.index.max() + pd.Timedelta(days=1), periods=14, freq='D')

# 创建未来14天的特征
# 假设 sku 是一个固定的值，或者你需要根据实际情况填充
future_sku = 'LO2312019AXNHS'
future_class_name = '卫衣/绒衫'
# 请根据实际情况修改
future_df = pd.DataFrame({
    'date_id': future_dates,
    'product_code': future_sku,
    'middle_class_name': future_sku,
    'day_of_week': future_dates.dayofweek,
    'day_of_month': future_dates.day,
    'month': future_dates.month
})
future_df["middle_class_name"] = future_df["middle_class_name"].astype("category").cat.codes
future_df['new_days'] = (future_df['date_id'] - df['first_new_date'].min()).dt.days
future_df["product_code"] = le.fit_transform(future_df["product_code"])
future_df["product_code"] = future_df["product_code"].astype("category").cat.codes

# 预测未来14天的销量
future_sales = model.predict(future_df[features], num_iteration=model.best_iteration)

# 将预测结果添加到 DataFrame
future_df['predicted_sales'] = future_sales

# 查看预测结果
print(future_df[['date_id', 'product_code', 'predicted_sales']])

# 可视化历史数据和预测结果
plt.figure(figsize=(12, 6))
plt.plot(df.index, df['sale_qty'], label='Historical Sales')
plt.plot(future_df['date_id'], future_df['predicted_sales'], label='Predicted Sales', linestyle='--', marker='o')
plt.xlabel('date_id')
plt.ylabel('Sales')
plt.title('Historical and Predicted Sales (LightGBM)')
plt.legend()
plt.show()
